Adam Richardson's Site

Common Lisp Notes

Table of Contents

<2023-04-19 Wed>

Resources

Source Files

  • Common Lisp source files can have the .lisp extension

Multi-Paradigm

  • ANSI Common Lisp is multi paradigm
  • It supports functional, generic, object oriented and domain specific programming styles.

SLIME

  • Common Lisp development environment for Emacs
  • The official webpage is, https://common-lisp.net/project/slime/
  • To use with org babel ensure that lisp is enabled in your org-babel-load-langauges
  • Start slime M-x slime before attempting to evaluate a common lisp source block with C-c C-c
  • You should be able to access variables and functions inside the SLIME REPL that are defined in the source code blocks
  • Use C-c C-d d to view the documentation for the symbol at point

CLISP

  • Easy to use and portable common lisp compiler that runs on most operating systems
  • Also provides debugger and interpreter

SBCL

  • Steel Bank Common Lisp descends from Carnegie Mellon, Steel being Andrew Carnegie's industry and Banking being Andrew Mellon's industry
  • Considered more heavy duty than CLISP
  • Also provides debugger and interpreter

SBCL Specific

Create a binary executable from a lisp image

  • Ensure that you are running sbcl without slime/swank (the multiple threads from that will break the binary export)
  • To create the binary use the following template:
(sb-ext:save-lisp-and-die "my-binary" :toplevel #'main :executable t)
  • This should create a binary that is the entire lisp image

Common Lisp Syntax

Anonymous (Lambda) Functions

  • This creates a lambda function that doubles the input (lambda (x) (* 2 x))

Cons Pairs (Dotted Lists) vs Lists

  • A list is really a chain of cons that ends with an empty list or nil
  • For exmaple '(1 2 3) is equal to (cons 1 (cons 2 (cons 3 nil))
  • Any cons cell that doesn't end in nil is known as a dotted list
  • (cons 1 2) is displayed by the REPL as (1 . 2)
  • You can create dotted lists with a quote as well, '(1 . 2)
  • You can make a list using quote and dot, '(1 . (2 . (3 . nil))) is equal to '(1 2 3)
  • (cdr '(1 . 2)) is 2 not (2)
  • (cdr '(1 2)) is (2) not 2
  • Cons pairs are useful for x/y coordinates and key value pairs

Circular Lists

  • When using circular lists you should set the global *print-circle* to true, (setf **print-circle** t)
  • The above enables complex printing features to be enabled when using self-referential lists
  • Below is an example of creating a circular list
(defparameter x '(1 2 3))
(setf (cdddr x) x)
  • You can use infinitely large index values in the above example
  • For instance (nth 1000 x) would return 2

Literals

Char

  • You can represent a character literal with a # followed by a backward slash and the character
  • For example #\a is the representation of the character a

Newline

  • #\newline

Tab

  • #\tab

Space

  • #\space

nil

  • nil can be represented with nil, 'nil, (), or '()
  • nil is the only Lisp object that is both a symbol and a list

Hex Numbers

  • #xFF

Symbols

  • Symbols in Common Lisp are case insensitive
  • It is common to only use lowercase when writing common lisp source code
  • To make a symbol that is case sensitive surround it with pipe, |, characters
    • This also allows you to use punctuation in a symbol name
    • |Ca$e mATT3rz| is an example
  • There are a variety of functions that can look up specific properties of a symbols
  • They all start with the symbol- prefix
  • For example symbol-value will show the value associated with a symbol
  • symbol-function will return the reference to the function a symbol points to
  • Be sure to quote the symbol name when using these functions, i.e. (symbol-value 'my-symbol)
  • Symbols are really just blocks of pointers that point to values, and functions
  • Because a symbol can have a value and function that allows functions and variables to not have name collisions

Quoting

  • Use the single quote ' to quote a list
  • To quasiquote use the backtick ` and the comma to unquote ,
  • `(one plus two is ,(+ 1 2))
  • The # before the quote is used to indicate the quoted symbol is a function
  • Common Lisp is a LISP-2 rather than a LISP-1 like Scheme
    • This means it has 2 separate scope for functions and data
    • You can have a function and data with the same symbol name
    • The caveat is you must use the # when quoting a function
  • When quasiquoting use a splicing command ,@ lists are spread when the quoted code is expanded

Variables

Globals

  • Use defparameter to create global variables
    • The defparameter function will mutate the value of a global if it already exists
  • It is a common practice to surround global variables with earmuffs (asterisks)
  • For example a global named num would look like this *num*
  • Another option to create globals is defvar
    • If the variable already exists defvar will not change the existing value

Local variables

  • Use let function to define local variables, you cannot reference other variables in the list
  • The let* function is the same as let but it lets you reference preceding variable names
(let* ((x 10)
       (y (* 2 x)))
  (+ x y))

Mutating

  • Use setf to change the value of an existing variable

Functions

Globals

  • Use defun to define a global function
(defun function_name (arguments)
  ...body)

Local Named Functions

  • To define a local function use the flet form
  • flet is very similar to let for variables but instead
(flet ((function_name (arguments)
         ...function body))
  ...body)
  • If you want to reference other local functions in an flet you should use labels instead
  • You can also labels to call a local named function recursively
(labels ((function_a (n)
           (+ n 2))
         (function_b (n)
           (function_a (function_a n))))
  (function_b 2))

Argument Limits

  • There is a limit on the number of arguments a function can have
  • You can check those limits in the Common Lisp REPL with call-arguments-limit

Optional Arguments

  • Adding &optional before an argument marks it as optional
  • This means that when the function is evaluated if you do not supply that argument it will not error
(defun fn-opt (not-optional &optional (val 10))
  (list not-optional val))

(fn-opt 100)
  • The above example shows an optional argument val with a default value of 10
  • It returns (100 10)

Variadic Arguments

  • Using &rest followed by a variable name collects a variable amount of arguments into a list
(defun fn-var (x &rest args)
  (append
   (if (listp x) x (list x))
   args))

(fn-var (fn-var 10) 100 10 10)
  • The above example returns the list (10 100 10 10)

Keyword Arguments

  • Using &key all of the arguments appearing after it will be named
  • To use a named argument it must be formatted with :<NAME_OF_ARG
  • The preceding colon is a keyword form
(defun fn-key (&key x y w h)
  (list x y w h))

(fn-key :w 20 :h 10 :x 5 :y 2)
  • The above example returns the list (5 2 20 10)
  • It shows how when the arguments are used as keywords the order does not matter

Body Argument

  • The &body is the same as &rest but it allows editors to indent differently for the remainder since it is a body
  • &body can only be used with macros

Default Values

  • Optional and keyword arguments can have a default value
  • To specify the default value you create a list with that starts with the local name of the argument followed by the default value
  • Additionally you can supply a third item to the list that is a boolean indicating if the value was set or not
(defun fn-default (&optional (val 12 val-set))
  (list val val-set))

(fn-default 100)
  • The above example returns the list (100 t)

Closures

  • Referencing variables in a lambda expression can prevent values from being garbage collected
  • This can be useful for memoizing functions
(let ((count 0))
  (defun say-hello-count ()
    (incf count)
    (format nil "Hello, for the ~a time" count)))

(say-hello-count)
(say-hello-count)

  • The above example shows using lexical binding to capture a closure variable inside the function
  • The count variable is incremented each time you call say-hello-count
  • The defun macro will allow you to call say-hello-count outside of the let

Get the function bound to a symbol

  • Use the symbol-function function to get the function bound to a symbol
  • This is useful if you want to create a higher order function that wraps an existing function and you need to store the original function

Tail Call Optimization

  • Tail call optimization is a way to get additional performance with recursive functions
  • Normally when a function has a recursive call it will add to the stack
  • If the function has too many recursive calls it can lead to a stack overflow
  • If the recursive call is the final statement in the function the lisp compiler can recognize this as a candidate for tail call optimization
  • The tail call optimization is the compiler not adding to the stack since you are already at the correct location
  • Not all lisp compilers support this since it is not part of the standard, unlike Scheme which requires tail call optimization
  • In clisp you need to explicitly compile the function to get the tail call optimization

Nullary Functions (Thunks)

  • A nullary function is a function that has no arguments
  • They are also commonly known as thunks or suspensions

Returning multiple values

  • It is possible to write a function that returns multiple values
  • When returning multiple values the first value returned is given preference when chaining the function
  • This behavior could be achieved with a list but if it is a special case when you need more than one value this could be cleaner

values

  • Use the values function to return multiple values from a function
(defun multi-v ()
  (values 'foo 'bar))

(multi-v)
  • The above example will return 'foo; 'bar

multiple-value-bind

  • To access all of the values returned by a function use multiple-value-bind
  • This allows you to name the values returned by the function and access them in a let style block
(multiple-value-bind (x y) ((lambda ()
                              (values 'foo 'bar)))
  (list x y))
  • The above example will return the list (foo bar)

Eval

  • You can evaluate a quoted symbol with eval
  • (eval '(+ 2 2)) should return 4
  • Like eval in JavaScript it can be a huge security risk in your program

Loops

loop

  • Loops can be a good alternative to recursion, especially when using an implementation that does not have tail recursion support
  • The most basic form of the loop is shown below
(loop
  (sexp)
  (sexp)
  ...
  (when (predicate)
    (return)))
  • The return command exits the loop
  • Loop can be used to create lists
  • Loop has a few keywords that tell it how to behave
  • The collect keyword specifies what you want to put into the returned list for this iteration
  • The repeat keyword indicates how many times a loop should run
(loop repeat 10
      collect 8)
  • The for and from / to keywords let you specify a variable local to the loop that increments each loop iteraction
  • This is inclusive and on both ends
(loop for n from 1 to 10
      collect n)
  • The above example will return (1 2 3 4 5 6 7 8 9 10)
  • The for and below keywords start from 0 and iterate through every integer less than the value after below
(loop for i below 10
      collect)
  • The above example will return (0 1 2 3 4 5 6 7 8 9)
  • There are many special tokens that can be passed to the loop macro that do special things
(loop for y below 10
      collect (loop for x below 10
                    collect (cons x y)))
  • The above example generates all of the 2d points between 0 - 10
  • Each row is a list of cons cells of points for a given y value
(defparameter *test* '(1 2 3))
(loop for i below (length *test*)
               do (princ (format nil "~d = ~d~%" i (nth i *test*))))
  • The above example returns the following
0 = 1
1 = 2
2 = 3

dotimes

  • dotimes takes a variable and an upper bound
  • It will perform the actions inside the body up to the upper bound
(dotimes (i 4)
  (princ (format nil "Loop #~d~%" i)))
  • The above example will print the following output
Loop #0
Loop #1
Loop #2
Loop #3

Macros

  • Use defmacro to define a lisp macro
  • Macro definition is similar to function definition
  • A macro usually return a quoted or quasi-quoted lisp expression that is expanded during compile time
  • In order to view an expanded use the macroexpand function
  • It is possible to define a macro that adds lexical variables, macros that do this are called anaphoric macros

Common Lisp Functions

Math

Incrementing

  • You can use 1- to decrement by 1, (1- 10) evaluates to 9
  • You can also use 1+ to increment by 1, (1+ 9) evaluates to 10
  • Additionally there are incf and decf to increment or decrement a value in place
  • Like setf the incf and decf functions must be called with a variable as a parameter
(defparameter n 0)
(incf n)
(incf n 2)
(decf n 3)
  • In the above example n starts at 0, then goes to 1, then to 2 and finally back to 0
  • The incf and decf functions also return the new value in addition to mutating the variable

Exponent

  • Use expt to raise a number to an exponent, (expt 53 53) raises 53 to the 53rd power

Random numbers

  • Use the random function to generate a random number
  • It takes the limit as an argument which can be either an int or a float
  • The returned number will be between 0 and the limit
  • If it is a float than the random number generated will also be a float
  • This has the side effect of changing the internal random-state

Arithmetic Shift (bitwise shift)

Round

  • The round function will take a number and return the two values, the rounded integer and the remainder
  • For example, (round 16.8) returns 17; -0.2
  • When rounding up the remainder will be negative

Modulus (Remainder)

  • Use the mod function to get the remainder between two numbers
(mod 5 2)
  • The above example returns 1

Floor / Integer Truncation

  • To get the integer truncation between two numbers use the floor function
(floor 5 2)
  • The above example returns 2

Largest and Smallest Integers

  • The Common Lisp standard defines variables for the largest and smallest integers
  • most-positive-fixnum is the largest integer
  • most-negative-fixnum is the smallest integer

Using / to get the reciprocal

  • When you use the / function with only a single value it will return the reciprocal
  • For example (/ 4) returns 1/4

Get the value of a bit in a number

  • To get the value of an individual bit you can use ldb and the byte function
  • For example (ldb (byte 1 3) 8) selects the 4th bit from the integer 8 which is 1

Strings

Concatenate

  • Use concatenate to join multiple strings together
  • Use the symbol 'string as the first argument to the function
  • (concatenate 'string "abc" "def") should return "abcdef"

Converting to and from character lists

  • Use coerce with either the list or string type
  • (coerce "ABC" 'list) should return (#\A #\B #\C)
  • (coerce '(#\A #\B #\C) 'string) should return "ABC"
  • This can be used with mapcar to iterate over each character
(mapcar #'(lambda (c)
            (princ (format nil "char: ~C~C" c #\newline)))
        (coerce "abc123" 'list))

Converting symbols to strings

  • Use prin1-to-string to convert symbols to strings
  • (prin1-to-string 'abc) should return "ABC"
  • The write-to-string function also can convert data to strings

Multi Line Strings

  • Lisp will store the newline character in the string
  • So the \n character is not needed for multi line strings
(setq mystring "the first line
the second line
and the third")
  • To insert a newline into a string use the format function and the ~C control character
  • The ~C control chacter means insert a character literal, you can use the #\newline character literal to get a new line
(format nil "hello~Cworld" #\newline)
  • Another way to do this is with ~%
(format nil "hello~%world")

Parsing an Integer from a String

  • The parse-integer function can be used to parse an integer from a string
  • The :radix keyword lets you specify the base of the number
  • The :junk-allowed keyword lets you indicate you want to just return nil if there is junk in the string
(parse-integer
 "beef"
 :radix 16
 :junk-allowed t)
  • The above example returns the integer 48879

Getting the Integer Code of an Char

  • The char-code function will return the integer ASCII code for the char
  • The code-char will give you the char for an integer ASCII code of a char
(code-char (char-code #\A))
  • The above example will return the character A
#\A

Turning a String into a Lisp Symbol

  • The intern function can turn a string into a Lisp symbol
(intern "abc")
  • The above example returns the symbol abc
  • There is some overlap between read and intern, in general if the intern functions does what you need then it is preferable since it is safer than the read function

Getting the Position of Char in String

  • The position function can be used to get the index of a char in a string, since a string is a sequence of characters
  • See Index of item in list

Creating a Fixed Size String

  • The make-string function will create a string with a given length
(make-string 10 :initial-element #\x)
  • The above example creates a string of length 10 filled with the char x

Changing capitalization of strings

(string-upcase "abc")
(string-downcase "ABC")
  • The string-upcase and string-downcase functions will change all letters in a string to desired capitalization
(string-capitalize "hello world")
  • The string-capitalize function wil capitalize the first letter of each word, the above example produces "Hello World"

Format

  • Wikipedia: Format (Common Lisp)
  • The format function takes 3 parameters, destination, control string and variadic values
  • The destination nil, t or some stream
  • When the destination is nil the format function returns the formatted string
  • When it is not nil it will return nil and send the output to either stdout with t or some other stream specified

Format Directives

  • c - single character
  • r - radix base
  • d - decimal (base 10) number
  • b - binary (base 2) number
  • o - octal (base 8) number
  • x - hexadecimal (base 16) number
  • f - floating point number
  • e - exponent notation for number
  • g - exponent or float, pickign automatically
  • $ - print with monetary conventions
  • a - print in human friendly manner
  • s - print symbol in format compatible with read function
  • w - print with printer control characters
  • i - indent a logical block
  • t - move cursor to column
  • p - prints singular or plural suffix
  • % - newline unconditionally, similar to terpi
  • & - newline conditionally, similar to fresh-line
  • v - iteration quantity, use this when you don't want to put the number in the format string

Examples

  • Strings
    (format nil "Your message is: ~a~%"
            "Hello, World")
    
  • Left pad zeroes
    (format nil "You number is: ~3,'0d," 12)
    
    • The above example will ensure that the decimal value is 3 digits long and will pad to the left with 0 if it is less than 3 digits
  • Looping with Format
    • You can loop through lists with format using ~{ and ~}
    (format nil "~{The list has: ~a~%~}" '("abc" "def" "ghi"))
    
  • Create a string of n length with repeated strings
    (format t "~v@{~a~:*~}" 20 "*")
    
    • The above example prints a string of 20 asterisks to standard output
    • The ~v tells format to get the quantity from the argument list
    • The ~@{ starts the loop but the @ means that the remaining arguments will be treated as a list, rather than expecting a list
    • The ~a prints the most appropriate string for the data element following the quantity, in this case *
    • The ~:* instructs format to go back one element in the argument list
      • This is needed since we don't have 20 different elements in the argument list
      • Going back after printing one causes it to continue to print the same string over and over
    • The ~} closes out the loop block
    (format t "~v@{~a~a~2:*~}" 20 "*" "_")
    
    • The modified above example will repeat *_ 20 times
    • The only change is that inside the loop we consume 2 items
    • Because we consume 2 we need to move back 2 and we do that with ~2:*
    • The : tells the * directive to move backwards
    • The quantity 2 before that tells it to move back that many items

Lists

push

  • Adds item to the beginning of a list
  • The list must be a variable
(defparameter *some-list* nil)

(push 4 *some-list*)
(push 3 *some-list*)
(push 2 *some-list*)
(push 1 *some-list*)
*some-list*

last

  • You can get the last element of a list with last

Using push to append

  • Since a list is just a cons pairs, creating a new cons pair with the last element will append
  • The cdr of the last element of a list is an empty list or nil
  • If you push into that empty list you will append to the list
  • For instance if a is (1 2 3) this should append 4 to the list (push 4 (cdr (last a)))

pushnew

  • The pushnew function will only add an item to a list if it is not already in it
(defparameter *some-list* '(1 2 3))

(pushnew 1 *some-list*)
  • In the above example 1 is already in the list so the results of pushnew is the same list (1 2 3)

member

  • Checks to see if an item is inside a list
  • (member 1 '(1 2 3 4))
  • This will return true when you check if nil is in the list

find

  • Use find to search through a list for the first item that matches
  • The search value is the first argument
  • The second argument is the list that is being searched
  • The keyword parameter passed with :key tells find how to determine if the list item matches the search
  • (find 20 '((a 5) (b 20) (c 6) (d 20)) :key #'cadr) should return (b 20)

find-if

  • Returns the first item in a list that satisifies the predicate
  • (find-if #'oddp '(2 4 5 6))
  • Returns nil if the item is not found
  • This will not work when searching for nil in a list

mapcar

  • Use mapcar to run a function on each element of a list
  • (mapcar (lambda (n) (1+ n)) '(1 2 3)) should return (2 3 4)
  • You can also run mapcar over multiple sequences
  • (mapcar (lambda (m n) (list m n)) '(1 2 3) '(a b c)) should return ((1 a) (2 b) (3 c))
  • mapc is a more efficient version of mapcar that does not return the list
  • maplist is another variant of mapcar that gives the remainder of the list as an argument to the function rather than a single item

mapcan

  • Similar to mapcar but allows the lambda to return a list
  • The list values are all appended together in the result
(mapcan (lambda (v)
          (case v
            (a '(1 2 3))
            (b '(4 5 6 7))
            (c '(8 9 10 11 12))))
        '(a c b))
  • The above example will return (1 2 3 8 9 10 11 12 4 5 6 7)

apply

  • Use apply to call a functions once with all the elements of a list as its arguments
(defparameter *rect* '(40 40 20 30))

(defun rect-area (x y w h)
  (* w h))

(apply #'rect-area *rect*)

remove-if-not

  • Removes all items from the list that do not satisfy the predicate
  • (remove-if-not #'oddp '(1 2 3 4 5)) should return (1 3 5)

nth

  • Use nth to get the value at index n from a list
  • (nth 2 '(7 8 9)) should return 9

use setf and nth to change list item value

  • You can use setf to mutate a list
  • For example if you have a list named l with the value (1 1 1)
  • (setf (nth 2 l) 4) should mutate l to be (1 1 4)

subseq

  • Use subseq to get a sub sequence of a list
  • The start index is required and you can optionally add the end index
  • The start index is inclusive and the end index is exclusive, (start end]
  • (subseq '(9 8 7 6) 1 3) should return (8 7)

Swapping list items with rotatef

  • If you have the list x with the value (1 2 3)
  • You can swap the 1 and with like so (rotatef (nth 0 x) (nth 2 x))
    • This should return (3 2 1)
  • This will mutate the list

concatenate

  • Use concatenate to join multiple lists together
  • Use the symbol 'list as the first argument to the function
  • (concatenate 'list '(1 2 3) '(4 5 6)) should return (1 2 3 4 5 6)

Slicing an item out of a list

  • You can generate a new list with a particular index sliced out using concatenate and subseq
  • If you have the list d with the value (1 2 3 4 5)
  • (concatenate 'list (subseq d 0 2) (subseq d 3)) should return (1 2 4 5)

Testing the values of a list with every some notevery notany

  • These functions run a predicate and return a different boolean value based on their rules
  • every returns nil at the first instance of a nil value, similar to logical and
  • some returns true if any of the values return true
  • notany returns nil if any of the values return true
  • notevery returns true if all the values are false
  • (every #'identity '(t t nil) should return nil
  • (every #'identity '(t t t)) should return true

substitute-if

  • Replaces every item in a sequence with the first argument if it passes the second argument predicate
  • For instance, (substitute-if 0 #'oddp '(1 2 3 4 5)) will return (0 2 0 4 0)

Index of item in list

  • The index of the first instance of an item in a list can be found with the position function
(position 2 '(1 2 3 4 2))
  • The above code will return 1 since that is the index of the first 2 in the list

Difference between lists

  • To get a list of the items that are difference between a list use the set-difference function
  • This function gives you the items that are in the first list that are not in the second list
  • (set-difference '(1 2 3) '(a 2 c)) should return the set (3 1), the order in the set does not necessarily match the order in the first list
  • (set-difference '(a 2 c) '(1 2 3)) should return the set (c a)

Intersection between lists

  • To get the intersection of two lists use the intersection function
  • (intersection '(1 2 3) '(a 2 c)) should return (2)

Remove Duplicates

  • The remove-duplicates function will return a list where no item repeats
  • You can give it a custom :test function to use when comparing items
  • (remove-duplicates '(1 1 2 2 3 3)) should return (1 2 3)

Appling a predicate to a list with some

  • The some function will run a predicate on each item in a list in order
  • The first time the predicate returns true the it stops checking the list
(some #'oddp '(2 2 4 8 6))
  • The above example will return nil since no members of the list are odd
(some #'oddp '(2 2 4 5 6))
  • The above example will return t since there is one odd member

reduce

  • Iterate through a sequence and reduce it down to a single value
  • The reduce function takes two arguments, a function that reduces two items to 1 and a sequence of items
(reduce #'* '(1 2 3 4 5 6 7))
  • The above example will return 5040, which is 1 * 2 * 3 * 4 * 5 * 6 * 7
  • In the reducer function the first argument is the accumulated value and the second is the current item in the list
  • It is possible to set an initial value for the accumulated value with the :initial-value key
(reduce (lambda (acc i)
          (if (oddp i)
              (+ acc (* i 2))
              acc))
        '(2 4 5 1)
        :initial-value 100)
  • The above example results in 112, since the initial value is 100 and the only two odd values in the list are 5 and 1 which * 2 are 12
  • The reduce function is generic and can be used on all sequence types (arrays, lists, strings)

sort

  • The sort function allows you to arbitrarily sort a list
(sort '(1 2 3 4 5 6) #'>)
  • The above example sorts the list in descending order

Association Lists (alists)

  • Use assoc to find the value of a key in an alist
(assoc 'mykey '((somekey (some-value))
                (mykey (my-value))
                (otherkey (other-value))))
  • alists can have multiple instances of a key inside them
  • When this happens assoc will return the first instance
  • If you push new keys into the alist you can overwrite the value of a key while preserving the previous value
  • You can use setf to change the value of an list, (setf (cadr (assoc '2 alist)) t)
  • alists are not very efficient beyond a dozen items
  • alists can also be implemented using cons pairs
(assoc 'mykey '((somekey . somevalue)
                (mykey . myvalue)
                (otherkey . othervalue)))

Logic / Conditionals

Complementing Predicates

  • If you have a predicate and you want the opposite of it or complement you can use the higher order function complement to achieve that
  • For example (substitute-if 0 (complement #'oddp) '(1 2 3 4 5)) should return (1 0 3 0 5)

Shortcut Boolean Evaluation

  • When evaluating an or or and boolean operator lisp will stop when it encounters the first symbol that evaluates to either true or false
  • For example when evaluating an or the first true that is encountered causes lisp to stop
  • When evaluating an and the first false encounted causes lisp to stop
  • This allows you to build conditionals out of or or and statements
  • and
    (defun pred_a ()
      t)
    
    (defun pred_b ()
      t)
    
    (defun work ()
      'work-when)
    
    (when (pred_a)
        (when (pred_b)
            (work)))
    
    (defun pred_a ()
      t)
    
    (defun pred_b ()
      t)
    
    (defun work ()
      'work-and)
    
    (and (pred_a) (pred_b) (work))
    
  • or
    (defun pred_a ()
      nil)
    
    (defun pred_b ()
      nil)
    
    (defun work ()
      'work-unless)
    
    (unless (pred_a)
      (unless (pred_b)
        (work)))
    
    (defun pred_a ()
      nil)
    
    (defun pred_b ()
      nil)
    
    (defun work ()
      'work-or)
    
    (or (pred_a) (pred_b) (work))
    

Using case to switch through multiple options

  • The case function can be used to handle multiple conditionals for a single value
  • A conditional named otherwise
(defvar x 1234)

(case x
  (123 (princ "x is 123"))
  (456 (princ "x is 456"))
  (otherwise (princ "beats me")))
  • The above example prints "beats me" since x does not match any of the conditionals

Equality

  • Use eq for comparing symbols
    • eq returns true when two symbols point to the same cons
    • eql will also return true when characters and numbers are used instead of symbols
  • Use equal for comparing everything else
    • equal will tell you if two things are isomorphic (look the same)
    • equalp will return true when strings have different capitalization, or numbers are not the same type (floats vs ints)
  • The = comparison is meant primarily for numbers
  • string-equal is specific for strings
  • char-equal is specific for chars

I/O

Printing and Reading

  • Use print to display a string on stdout
    • This will automatically add a new line at the end of the string
    • prin1 and princ will not add the newline
    • print will print values as they are stored in Lisp, so strings will have quotes and literals are displayed as such
      • (print #\newline) will actually print #\newline to stdout
    • Use princ to not add the quotation marks and use the characters the literals represent
      • (princ #\newline) will just print an empty line
    • The goal of print is to output data in a way that it could be re-read back into its internal representation
  • Use read to read from stdin
    • This function is called with no arguments and returns after the user has typed something and pressed enter
    • You can use this to assign the value into a variable: (let ((user-input (read))))
  • Both print and read can handle any Lisp data type, including symbols
  • Use read-line to read the input as a string only rather than any valid Lisp data
  • Use the function fresh-line to print a new line if the cursor is not at the beginning of a line: (fresh-line)
  • The terpri function will always print a new line even if the cursor is at the beginning of the line
  • The read-sequence function will fill a sequence with items from a stream

Files

  • with-open-file optionally accepts a steam and file name to open a file
  • If you do not pass in an existing stream a new one is created
  • With the stream variable print functions can send their output to that file
  • If the stream is *standard-output* then the print functions will automatically send the output to the file
(with-open-file (stream
                 "~/tmp/testfile.txt"
                 :direction :output
                 :if-exists :supersede)
  (princ "Hello World!" stream)
  (princ #\newline stream))
  • The below example shows routing standard output to a file and appending to the file
  • The finish-output function will empty the buffers into the file
(with-open-file (*standard-output*
                 "~/tmp/test.log"
                 :direction :output
                 :if-does-not-exist :create
                 :if-exists :append)
  (loop repeat 100 for x from 0
        do (progn
             (fresh-line)
             (format t "loop #~d" x)
             (finish-output)
             (sleep 1))))
  • There is a global stream *standard-ouput* that represents stdout of the lisp environment

Arrays

  • Any one dimensional array is also known as a vector

make-array

  • The make-array function is used to create an array
  • It takes an argument that specifies the size of the array
  • It will return an array that size with each element initialized to nil

aref

  • To access a member of an array use aref
  • For example, say you have some-list that has this value, #(1 2 3) to access array index 1 with aref you would do this, (aref some-list 1), this will return 2
  • aref can be combined with setf to mutate an array
  • (setf (aref some-list 1) 5) will mutate some-list into #(1 5 3)

Hash Tables

make-hash-table

  • The make-hash-table function will create an empty hash table

gethash

  • The gethash function is used to access items from a hash table
  • The first argument of the gethash function is the key, the second is the hash table
  • (gethash 'foo some-hash-table) will access the foo key in the hash table some-hash-table
  • This can also be used with setf to set values in the hash table, (setf (gethash 'foo some-hash-table) 'bar)
  • The gethash function returns multiple values, the first is the value stored in the hash-table for the key, the second is whether or not that key was in the table
  • This is needed since a key could be present in the table but have nil set as its value

Key Equality

  • By default hash tables use eq to compare equality
  • This will cause issues when you want to use a symbol as a key
  • The equality function in a hash table can be changed using the :test key in make-hash-table
  • (make-hash-table :test #'equal) lets you define a table whose keys are compared with the equal function instead of eq
  • This is useful when you want to have keys are cons pairs

Removing entries with remhash

  • The remhash function mirrors the gethash function but it removes the key from the table
(defparameter my-hash (make-hash-table))
(setf (gethash 'foo my-hash) 'bar)
(remhash 'foo my-hash)
  • The above example adds and removes a key from the my-hash table

Profiling

time

  • The time function will perform the argument function and return a lot of use profile information
Real time: 0.73878 sec.
Run time: 0.738236 sec.
Space: 31004976 Bytes
GC: 26, GC time: 0.098185 sec.
  • The above is an example of some of the profile data that the time function will return

Generics

setf

  • In general the code for getting data out of something is the same as code for putting something in
  • The setf command is a generic setter that can put data into data structures using the accessor as an argument
  • The following setf example changes the third item of the list foo, (setf (third foo) 'bar)
  • The first parameter in setf is a generalized reference
  • A generalized reference parameter can be arbitrarily complicated, meaning whatever path needed to access the reference you will still be able to mutate it with setf

Type Predicates

  • Common Lisp has dynamic typing, so a symbol can be any type
  • This is a list of all the type predicates:
    • arrayp
    • characterp
    • consp
    • functionp
    • hash-table-p
    • listp
    • stringp
    • symbolp
    • atom (this is the opposite of consp, anything that is not a cons cell matches)
    • null Only returns true if input is nil, same as the not predicate
  • These predicates an be used to implement generic functions

defmethod

  • The defmethod macro will allow you to define separate functions for each supported argument type
  • This can help with code readability so you don't have have a long conditional that checks the type of the arguments
  • When using defmethod you need to explicitly state the type of each argument
(defmethod add ((x number) (y number))
  (+ x y))

(defmethod add ((x string) (y string))
  (format nil "string add:~%~4tx: ~a~%~4ty: ~a~%" x y))

(add (format nil "num add: ~d" (add 100 -100))
     (format nil "num add: ~d" (add 2 2)))
  • The above example will produce the following ouput
string add:
    x: num add: 0
    y: num add: 4

type-of

  • The type-of function will return the type of the variable
  • For example, (type-of 12) will return integer
  • (type-of 'hello) will return symbol

Exceptions / Errors

  • To generate an exception or error use the error function
(error "this will generate an error")
  • The above example shows generating a simple error with a custom error message

Custom Error Conditions

  • The function define-condition allows you to define a custom error condition
(define-condition my-error-condition () ()
  (:report (lambda (condition stream)
             (princ "custom report message for `my-error-condition'" stream))))

(error 'my-error-condition)
  • The above example creates a custom error condition with a customized report
  • Use the error function and the symbol of the condition to trigger it

Handling Errors

  • The handler-case function allows you to intercept an error
(define-condition my-error-condition () ()
  (:report (lambda (condition stream)
             (princ "custom report message for `my-error-condition'" stream))))

(defun going-to-error ()
  (error 'my-error-condition))

(handler-case (going-to-error)
  (my-error-condition () "Intercepted `my-error-condition'"))
  • The above example shows using handler-case to intercept a particular error symbol

Generate Random Symbols

  • Sometimes its useful to generate a random symbol
  • This can be handy for macros that need to lexically bind a value but don't want to accidentally have variables shadowed by lisp forms supplied by the user
  • The gensym function will return a random unique symbol

Sleeping

  • Common Lisp has a sleep function that takes the number of seconds you want to sleep as an argument

Common Lisp Data Structures

List

  • Most fundamental data structure in a lisp
  • A series of nested cons pairs terminated with nil
  • The access time for elements inside lists is not constant

Array

  • Arrays are like like lists except the access time for any element is constant
  • An array literal is preceded with a # to distinguish it from a list, for example, #(1 2 3)
  • Arrays are in general faster than lists when accessing or setting specific elements

Hash Table

  • A hash table is very similar to an alist
  • A hash table literal is preceded with a #S to distinguish it from a list and an array
  • Similar to arrays hash tables have a constant look up time
  • Hash tables are less efficient than alists for really small tables
  • Very large hash tables can be paged out to virtual memory which could have poor performance
  • Lisp will sometimes need to reallocate the memory for the hash table when inserting a key
  • This will cause an occasional slow key insertion
  • Both arrays and hash tables are not considered very lispy
  • They are best avoided until performance concerns arise

Structures

defstruct

  • The defstruct macro is useful for building structured data out of lists
  • The defstruct macro will also create functions for building and accessing the data from the struct
(defstruct rectangle
  x
  y
  width
  height)

(defparameter *my-rect* (make-rectangle :x 10 :y 10 :width 50 :height 25))

(rectangle-height *my-rect*)
(setf (rectangle-x *my-rect*) 85)
*my-rect*
  • The above example shows creating a structure with defstruct and building one with the make-<STRUCT_NAME> function
  • It also shows using a generated accessor function <STRUCT_NAME>-<PROPERPTY_NAME>
  • The above example produces the following struct literal, #S(RECTANGLE :X 85 :Y 10 :WIDTH 50 :HEIGHT 25)

Default Values

  • It is possible to provide default values to a struct by wrapping the slot with parenthesis and providing a value
(defstruct rectangle
  (x 10)
  (y 10)
  (width 100)
  (height 100))

(make-rectangle)
  • The above example shows defining a struct with default values
  • When initializing the struct the slots can be omitted and they will use the default values
  • The above example returns the following struct literal, #S(RECTANGLE :X 10 :Y 10 :WIDTH 100 :HEIGHT 100)

Including Another Structure

  • It is possible to declare a struct that includes the slots from another struct
  • The syntax for doing this is to wrap the structure name in parenthesis then add a (:include some-other-struct) inside
(defstruct point
  (x 5)
  (y 5))

(defstruct (circle (:include point))
  (radius 4))

(make-circle :x 25 :y 30 :radius 10)
  • The above example shows creating a circle structure that inherits all the slot from the point struct
  • This example returns this struct literal, #S(CIRCLE :X 5 :Y 5 :RADIUS 4)

Streams

  • There are two types of streams input and output
  • The predicates input-stream-p and output-stream-p will indicate which direction a stream is
  • The standard print and read functions work with streams
  • Streams support most of the functions that lists do with the exception of setf
  • A string stream is useful for testing code that operates on streams
(with-output-to-string (*standard-output*)
  (princ "Hello world!"))
  • The above example shows a with form that will collect anything that would normally be sent to standard output and instead return it as a string

Quick Lisp

  • Quicklisp is a library manager (package manager) for Common Lisp

Installation

  • Download quicklisp.lisp form their website, curl -O https://beta.quicklisp.org/quicklisp.lisp
  • Download the PGP signature from their website, curl -O https://beta.quicklisp.org/quicklisp.lisp.asc
  • Download the release signing public key, curl -O https://beta.quicklisp.org/release-key.txt
  • Import the release signing key, gpg --import release-key.txt
  • Verify the quicklisp.lisp file, gpg --verify quicklisp.lisp.asc quicklisp.lisp
  • Start up a Common Lisp environment
  • Inside the REPL run, (load "quicklisp.lisp") (Ensure the REPL was started in the same folder Quicklisp was downloaded to)
  • Run (quicklisp-quickstart:install) to install Quicklisp
  • This will create a quicklisp folder in your home directory
  • Normally when starting a new lisp session you would run (load "~/quicklisp/setup.lisp) to load Quicklisp into the running session
  • Use (ql:add-to-init-file) to have this happen automatically
  • Install the slime helper, (ql:quickload "quicklisp-slime-helper")
    • Be sure to follow the instructions

Installing Libraries

  • This is an example of installing a package, (ql:quickload "vecto")
  • This is an example of uninstalling a package, (ql:uninstall "vecto")
  • To search for a library to install use apropos, (ql:system-apropos "sdl2")

Upgrading Quicklisp

  • Use (ql:update-dist "quicklisp") to update Quicklisp
  • Run (ql:update-client) to update the client

Local Projects

  • To use the Common Lisp systems create symlinks to the ~/quicklisp/local-projects folder for folders underneath the lisp/ directory
  • Once the symlinks are loaded inside the REPL run (ql:register-local-projects) for quicklisp to know about the system
  • You should be able to use (ql:quickload "my-local-project") to load your system

Debugging

(declaim (optimize (debug 3)))
  • To step through a function use the step higher order function
(step (my-fun '(a b c)))
  • You might need to recompile the code you are inspecting after setting the debug level

Examples

Split String into List of Words

(defun split-by-one-space (string)
    "Returns a list of substrings of string
divided by ONE space each.
Note: Two consecutive spaces will be seen as
if there were an empty string between them."
    (loop for i = 0 then (1+ j)
          as j = (position #\Space string :start i)
          collect (subseq string i j)
          while j))
  • Alternatively use cl-ppcre, (cl-ppcre:split #\Space some-string)

Lisp Library Notes